ostree-prepare-root: Use pivot_root if real sysroot is already mounted at /
authorWilliam Manley <will@williammanley.net>
Tue, 19 Jul 2016 18:48:23 +0000 (19:48 +0100)
committerAtomic Bot <atomic-devel@projectatomic.io>
Tue, 2 Aug 2016 19:07:25 +0000 (19:07 +0000)
This allows ostree-prepare-root outside of the initramfs context where the
real rootfs is already mounted at /.  We can't use `mount --move` in this
case because we would be trying to move / into a subdirectory of itself.

Closes: #403
Approved by: cgwalters

src/switchroot/ostree-prepare-root.c
tests/test-switchroot.sh

index 980e69fcec1b0d1c1652ce15a6ec2f71ed0476fe..0d5ba35e8c372aadf557c7de1e2b22f48c82723a 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/param.h>
+#include <sys/syscall.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -184,6 +185,12 @@ resolve_deploy_path (const char * root_mountpoint)
   return deploy_path;
 }
 
+static int
+pivot_root(const char * new_root, const char * put_old)
+{
+  return syscall(__NR_pivot_root, new_root, put_old);
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -197,6 +204,7 @@ main(int argc, char *argv[])
   else
     root_mountpoint = argv[1];
 
+  root_mountpoint = realpath (root_mountpoint, NULL);
   deploy_path = resolve_deploy_path (root_mountpoint);
 
   /* Work-around for a kernel bug: for some reason the kernel
@@ -292,41 +300,58 @@ main(int argc, char *argv[])
 
   touch_run_ostree ();
 
-  /* In this instance typically we have our ready made-up up root at
-   * /sysroot/ostree/deploy/.../ (deploy_path) and the real rootfs at
-   * /sysroot (root_mountpoint).  We want to end up with our made-up root at
-   * /sysroot/ and the real rootfs under /sysroot/sysroot as systemd will be
-   * responsible for moving /sysroot to /.
-   *
-   * We need to do this in 3 moves to avoid trying to move /sysroot under
-   * itself:
-   *
-   * 1. /sysroot/ostree/deploy/... -> /sysroot.tmp
-   * 2. /sysroot -> /sysroot.tmp/sysroot
-   * 3. /sysroot.tmp -> /sysroot
-   */
-  if (mkdir ("/sysroot.tmp", 0755) < 0)
+  if (strcmp(root_mountpoint, "/") == 0)
     {
-      perrorv ("couldn't create temporary sysroot /sysroot.tmp: ");
-      exit (EXIT_FAILURE);
+      /* pivot_root rotates two mount points around.  In this instance . (the
+       * deploy location) becomes / and the existing / becomes /sysroot.  We
+       * have to use pivot_root rather than mount --move in this instance
+       * because our deploy location is mounted as a subdirectory of the real
+       * sysroot, so moving sysroot would also move the deploy location.   In
+       * reality attempting mount --move would fail with EBUSY. */
+      if (pivot_root (".", "sysroot") < 0)
+        {
+          perrorv ("failed to pivot_root to deployment");
+          exit (EXIT_FAILURE);
+        }
     }
-
-  if (mount (deploy_path, "/sysroot.tmp", NULL, MS_MOVE, NULL) < 0)
+  else
     {
-      perrorv ("failed to MS_MOVE '%s' to '/sysroot.tmp'", deploy_path);
-      exit (EXIT_FAILURE);
-    }
+      /* In this instance typically we have our ready made-up up root at
+       * /sysroot/ostree/deploy/.../ (deploy_path) and the real rootfs at
+       * /sysroot (root_mountpoint).  We want to end up with our made-up root at
+       * /sysroot/ and the real rootfs under /sysroot/sysroot as systemd will be
+       * responsible for moving /sysroot to /.
+       *
+       * We need to do this in 3 moves to avoid trying to move /sysroot under
+       * itself:
+       *
+       * 1. /sysroot/ostree/deploy/... -> /sysroot.tmp
+       * 2. /sysroot -> /sysroot.tmp/sysroot
+       * 3. /sysroot.tmp -> /sysroot
+       */
+      if (mkdir ("/sysroot.tmp", 0755) < 0)
+        {
+          perrorv ("couldn't create temporary sysroot /sysroot.tmp: ");
+          exit (EXIT_FAILURE);
+        }
 
-  if (mount (root_mountpoint, "sysroot", NULL, MS_MOVE, NULL) < 0)
-    {
-      perrorv ("failed to MS_MOVE '%s' to 'sysroot'", root_mountpoint);
-      exit (EXIT_FAILURE);
-    }
+      if (mount (deploy_path, "/sysroot.tmp", NULL, MS_MOVE, NULL) < 0)
+        {
+          perrorv ("failed to MS_MOVE '%s' to '/sysroot.tmp'", deploy_path);
+          exit (EXIT_FAILURE);
+        }
 
-  if (mount (".", root_mountpoint, NULL, MS_MOVE, NULL) < 0)
-    {
-      perrorv ("failed to MS_MOVE %s to %s", deploy_path, root_mountpoint);
-      exit (EXIT_FAILURE);
+      if (mount (root_mountpoint, "sysroot", NULL, MS_MOVE, NULL) < 0)
+        {
+          perrorv ("failed to MS_MOVE '%s' to 'sysroot'", root_mountpoint);
+          exit (EXIT_FAILURE);
+        }
+
+      if (mount (".", root_mountpoint, NULL, MS_MOVE, NULL) < 0)
+        {
+          perrorv ("failed to MS_MOVE %s to %s", deploy_path, root_mountpoint);
+          exit (EXIT_FAILURE);
+        }
     }
 
   if (getpid() == 1)
index e039f5b7dcc34e77de5770ad53341cc9f1e00e9f..03e828082ff941d5d90084ad850d645153da3d56 100755 (executable)
@@ -76,12 +76,32 @@ test_that_prepare_root_sets_sysroot_up_correctly_with_initrd() {
        echo "ok ostree-prepare-root sets sysroot up correctly with initrd"
 }
 
+setup_no_initrd_env() {
+       mount --bind "$1" "$1"
+       setup_rootfs "$1"
+       setup_bootfs "$1"
+}
+
+test_that_prepare_root_sets_root_up_correctly_with_no_initrd() {
+       find_in_env setup_no_initrd_env >files
+
+       grep -qx "/this_is_ostree_root" files
+       grep -qx "/sysroot/this_is_bootfs" files
+       grep -qx "/sysroot/this_is_real_root" files
+       grep -qx "/var/this_is_ostree_var" files
+       grep -qx "/usr/this_is_ostree_usr" files
+
+       grep -qx "/usr is not writable" files
+       echo "ok ostree-prepare-root sets root up correctly with no initrd"
+}
+
 # This script sources itself so we only want to run tests if we're the parent:
 if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
        . $(dirname $0)/libtest.sh
        unshare -m true || \
            skip "this test needs to set up mount namespaces, rerun as root"
 
-       echo "1..1"
+       echo "1..2"
        test_that_prepare_root_sets_sysroot_up_correctly_with_initrd
+       test_that_prepare_root_sets_root_up_correctly_with_no_initrd
 fi